#pragma once
#include "Log.hpp"
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ThreadPool.hpp"
#include "Task.hpp"
#include <sys/wait.h>
#include <unistd.h>
using namespace std;

const int defaultfd = -1;
const int defaultport = 8080;
const string defaultip = "0.0.0.0";
const int backlog = 10; // 直接用，一般不要设置的太大。

class TcpServer; // 声明

class ThreadData
{
public:
    ThreadData(int fd, string ip, uint16_t port, TcpServer *const t)
        : sockfd_(fd), clientip_(ip), clientport_(port), t_(t)
    {
    }

public:
    int sockfd_;
    string clientip_;
    uint16_t clientport_;

public:
    TcpServer *const t_;
};

Log lg;

enum
{
    SockError = 2,
    BindError,
    ListenError
};

class TcpServer
{
public:
    TcpServer(int port = defaultport, string ip = defaultip, int sockfd = defaultfd)
        : listensockfd_(sockfd), ip_(ip), port_(port)
    {
    }

    void InitServer()
    {
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensockfd_ < 0)
        {
            lg(Fatal, "create socket, errno: %d, strerror: %s", errno, strerror(errno));
            exit(SockError);
        }
        //
        lg(Info, "create socket success, sockfd: %d", listensockfd_);

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));

        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr)); // 主机序列转网络学列。 inet_aton是一个线程安全的函数。

        // 绑定
        if (bind(listensockfd_, (sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BindError);
        }
        // tcp面向字节流， 是被动的， 所以要将对应的socket设置为监听状态。
        if (listen(listensockfd_, backlog) < 0) // backlock表示的是底层全连接队列的长度。 这个参数对意思， 不做解释。
        {
            lg(Fatal, "Listen error, errno: %d, strerror: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "Listen has success");
    }

    void Start()
    {
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info, "tcpServer is running...");
        for (;;) // tcp协议也是一种一直处于运行的服务
        {
            // tcp是面向连接的， 所以他比udp还多了一步accept， 先将客户端与服务端连接起来。accept的返回值成功返回整数文件描述符，否则-1被返回， 错误码被设置
            // 1、获取新连接，
            struct sockaddr_in client; // 获取的是客户端的addr
            socklen_t len = sizeof(client);

            int sockfd = accept(listensockfd_, (sockaddr *)&client, &len); // accept成功， 就能知道是谁连接的我。
            if (sockfd < 0)                                                // 关于这两个套接字， sockfd_的核心工作就只是把链接获取上来， 未来进行IO操作， 看的是sockfd。
            {
                lg(Waring, "listen error, errno: %d, strerror: %s", errno, strerror(errno));
                continue;
            }
            uint16_t clientport = ntohs(client.sin_port); // 网络序列转主机序列
            char clientip[32];
            inet_ntop(sockfd, &client.sin_addr, clientip, sizeof(clientip));
            // 2、根据新连接进行通信
            lg(Info, "get a new link..., sockfd:%d, clientport: %d, clientip: %s", sockfd, clientport, clientip);

            // version--4线程池版本
            Task task_(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(task_);
        }
    }

    ~TcpServer()
    {
    }

private:
    int listensockfd_; // 监听套接字， 只用来升起服务器， 接收链接

    uint16_t port_;
    string ip_;
};